// We have include guard on executor.h
#include "executor.hpp"

namespace coro {

    // Start an new corotine
    // spawn should only be run on the main thread
    // Fn should be void(Context<ValType> &)
    template <typename ValType, typename Fn>
    void Executor::spawn(Fn fn)
    {
        Context<ValType> *_ctx = nullptr;
        typename coro_t<ValType>::push_type *push_type =
            new typename coro_t<ValType>::push_type([this, fn, &push_type, &_ctx](typename coro_t<ValType>::pull_type &pull_type) {
                // This function is called at the first time that push_type::operator()
                Context<ValType> *ctx = new Context<ValType>(push_type, pull_type, this);
                this->register_context(ctx);
                _ctx = ctx;
                // Switch to the main context, we will start the fn next time
                ctx->yield();
                fn(*ctx);
            });
        // Start the coroutine to initialize the Context
        (*push_type)(std::optional<ValType>());
        // Now tell the Executor that this context is ready to schedule
        {
            std::unique_lock locker(m_lock);
            m_waked_ctxs.push_back(std::make_pair<>(_ctx, std::optional<void *>()));
        }
    }

    template<typename ValType>
    void Executor::wake(Context<ValType> *ctx, ValType val)
    {
        // Save the value to heap
        ValType *pval = new ValType(val);
        m_lock.lock();
        m_waked_ctxs.push_back(std::make_pair<>(ctx, (void *)pval));
        m_lock.unlock();

        uint64_t _v = 1;
        int r = write(m_event_fd, (void *)&_v, sizeof(_v));
        if (r < 0) {
            throw std::runtime_error((boost::format("failed to notify event fd: %1%")
                                        % utils::strerror(errno)).str());
        }
    }

    template<typename Fn>
    int Executor::io_schedule(int fd, int events, Fn fn)
    {
        EvtCb cb = fn;
        return io_schedule_internal(fd, events, cb);
    }

    template <typename ValType>
    void WakerImpl<ValType>::wake_impl(ValType val)
    {
        m_executor->wake(m_ctx, val);
    }

};
